package main

import (
	"encoding/json"
	"fmt"
	"time"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

/**
假设你要开发一个博客系统，有以下几个实体： User （用户）、 Post （文章）、 Comment （评论）。
- 使用Gorm定义 User 、 Post 和 Comment 模型，其中 User 与 Post 是一对多关系（一个用户可以发布多篇文章），
	Post 与 Comment 也是一对多关系（一篇文章可以有多个评论）。,
- 编写Go代码，使用Gorm创建这些模型对应的数据库表。

关联查询:基于上述博客系统的模型定义。
- 编写Go代码，使用Gorm查询某个用户发布的所有文章及其对应的评论信息。,
- 编写Go代码，使用Gorm查询评论数量最多的文章信息。

钩子函数：继续使用博客系统的模型。
- 为 Post 模型添加一个钩子函数，在文章创建时自动更新用户的文章数量统计字段。,
- 为 Comment 模型添加一个钩子函数，在评论删除时检查文章的评论数量，如果评论数量为 0，则更新文章的评论状态为 "无评论"。
*/

var Db *gorm.DB

type User struct {
	gorm.Model
	Name      string
	Email     string
	Age       uint8 //默认是0
	Phone     string
	PostCount uint32
	Posts     []*Post
}

type Post struct {
	gorm.Model
	Title        string     //标题
	Content      string     //内容
	PubTime      *time.Time //发布时间
	CommentCount uint32     //默认是0
	CommentState string
	Comments     []*Comment
	UserID       *uint
	User         *User
}

type Comment struct {
	gorm.Model
	Content string //评论内容
	PostID  *uint
	Post    *Post
	UserID  *uint
	User    *User
}

func (u *User) pubPost(p *Post) {
	p.UserID = &u.ID
	p.User = u
	Db.Create(&p)
}

func (u *User) pubComment(p *Post, c Comment) {
	c.UserID = &u.ID
	c.User = u
	c.PostID = &p.ID
	c.Post = p
	Db.Create(&c)
}

/*
*
为 Post 模型添加一个钩子函数，在文章创建时自动更新用户的文章数量统计字段
*/
func (p *Post) AfterCreate(tx *gorm.DB) (err error) {
	var count int
	//查询最新数量
	tx.Raw("select count(*) from posts where user_id=? and deleted_at is null", p.UserID).Scan(&count)

	var user User
	tx.Model(&user).Where("id=?", p.UserID).Update("post_count", count)
	// +1
	return nil
}

/*
*
 */
func (c *Comment) AfterCreate(tx *gorm.DB) (err error) {
	_, e := c.updateCount(tx)
	return e
}

/*
*
为 Comment 模型添加一个钩子函数，在评论删除时检查文章的评论数量，如果评论数量为 0，则更新文章的评论状态为 "无评论"。
*/
func (c *Comment) AfterDelete(tx *gorm.DB) (err error) {
	count, e := c.updateCount(tx)
	if count == 0 {
		post := &Post{}
		tx.Model(&post).Where("id=?", c.PostID).Update("comment_state", "无评论")
	}
	return e
}

func (c *Comment) updateCount(tx *gorm.DB) (res int, err error) {
	var count int
	tx.Raw("select count(*) from comments where post_id=? and deleted_at is null", c.PostID).Scan(&count)

	var post Post
	tx.Model(&post).Where("id=?", c.PostID).Update("comment_count", count)
	return count, nil
}

func init() {
	db, err := gorm.Open(mysql.New(mysql.Config{
		DSN:                       "root:123456@tcp(127.0.0.1:3306)/gorm_blog?charset=utf8&parseTime=True&loc=Local",
		DefaultStringSize:         256,   // string 类型字段的默认长度
		DisableDatetimePrecision:  true,  // 禁用 datetime 精度，MySQL 5.6 之前的数据库不支持
		DontSupportRenameIndex:    true,  // 重命名索引时采用删除并新建的方式，MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
		DontSupportRenameColumn:   true,  // 用 `change` 重命名列，MySQL 8 之前的数据库和 MariaDB 不支持重命名列
		SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
	}), &gorm.Config{})
	if err != nil {
		panic(err)
	}
	Db = db

	initData()
}

/*
*
初始化数
*/
func initData() {
	Db.AutoMigrate(&User{}, &Post{}, &Comment{})
	user1 := User{
		Name:  "曹雪芹",
		Email: "cxq@qq.com",
		Age:   21,
		Phone: "17612344321",
	}

	user2 := User{
		Name:  "吴承恩",
		Email: "wce@qq.com",
		Age:   35,
		Phone: "13912344321",
	}

	user3 := User{
		Name:  "罗贯中",
		Email: "lgz@qq.com",
		Age:   42,
		Phone: "18912344321",
	}
	users := []*User{&user1, &user2, &user3}
	Db.Create(users)

	now := time.Now()
	p1 := Post{
		Title:   "西游记",
		Content: "西游记内容XXXX",
		PubTime: &now,
	}
	p2 := Post{
		Title:   "红楼梦",
		Content: "红楼梦内容XXXX",
		PubTime: &now,
	}
	p3 := Post{
		Title:   "水浒传",
		Content: "水浒传内容XXXX",
		PubTime: &now,
	}
	p4 := Post{
		Title:   "三国演义",
		Content: "三国演义内容XXXX",
		PubTime: &now,
	}
	p5 := Post{
		Title:   "三国演义",
		Content: "三国演义内容XXXX",
		PubTime: &now,
	}
	p6 := Post{
		Title:   "三国演义",
		Content: "三国演义内容XXXX",
		PubTime: &now,
	}
	user1.pubPost(&p1)
	user2.pubPost(&p2)
	user2.pubPost(&p3)
	user3.pubPost(&p4)
	user3.pubPost(&p5)
	user3.pubPost(&p6)

	c1 := Comment{
		Content: "写得还行",
	}
	c2 := Comment{
		Content: "写得还行,继续加油",
	}
	c3 := Comment{
		Content: "666",
	}
	c4 := Comment{
		Content: "牛逼",
	}
	c5 := Comment{
		Content: "太棒了",
	}

	user1.pubComment(&p2, c1)
	user2.pubComment(&p1, c2)
	user3.pubComment(&p3, c3)
	user3.pubComment(&p2, c4)
	user3.pubComment(&p2, c5)

	fmt.Println("数据初始化完成")
}

func main() {
	//查询某个用户发布的所有文章及其对应的评论信息。
	var user = &User{}
	Db.Preload("Posts").Preload("Posts.Comments").Find(&user, 1)
	fmt.Println(user)
	if len(user.Posts) > 0 {
		for _, post := range user.Posts {
			fmt.Println(post)
			if len(post.Comments) > 0 {
				for _, comment := range post.Comments {
					fmt.Println(comment)
				}
			}
		}
	}

	ujson, err := json.Marshal(user)
	if err != nil {
		fmt.Println("json err ", err)
	}
	fmt.Println(string(ujson))

	//查询评论数量最多的文章信息
	post := &Post{}
	Db.Order("comment_count desc").First(&post)

	pjson, err := json.Marshal(post)
	if err != nil {
		fmt.Println("json err ", err)
	}
	fmt.Println(string(pjson))

	var comments []*Comment
	Db.Where("post_id = ?", 2).Find(&comments)
	for _, comment := range comments {
		Db.Delete(&comment)
	}
	post2 := &Post{}
	Db.First(&post2, 2)
	fmt.Println(post2)

	pjson2, err := json.Marshal(post2)
	if err != nil {
		fmt.Println("json err ", err)
	}
	fmt.Println(string(pjson2))
}
